#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    labelSocketState = new QLabel("Socket状态", ui->statusBar);
    labelSocketState->setMinimumWidth(150);
    ui->statusBar->addWidget(labelSocketState);

    udpSocket = new QUdpSocket(this);
    /* TLL: Time to live 生存时间
     * 每跨过一个路由就-1，默认1就是本地子网
     */
    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);
    connect(udpSocket, &QUdpSocket::stateChanged, this, [this](QAbstractSocket::SocketState state) {
        switch (state)
        {
        case QAbstractSocket::SocketState::UnconnectedState:
            this->labelSocketState->setText("socket状态 UnconnectedState");
            break;
        case QAbstractSocket::SocketState::HostLookupState:
            this->labelSocketState->setText("socket状态 HostLookupState");
            break;
        case QAbstractSocket::SocketState::ConnectingState:
            this->labelSocketState->setText("socket状态 ConnectingState");
            break;
        case QAbstractSocket::SocketState::ConnectedState:
            this->labelSocketState->setText("socket状态 ConnectedState");
            break;
        case QAbstractSocket::SocketState::BoundState:
            this->labelSocketState->setText("socket状态 BoundState");
            break;
        case QAbstractSocket::SocketState::ListeningState:
            this->labelSocketState->setText("socket状态 ListeningState");
            break;
        case QAbstractSocket::SocketState::ClosingState:
            this->labelSocketState->setText("socket状态 ClosingState");
            break;
        default:
            break;
        }
    });
    connect(udpSocket, &QUdpSocket::readyRead, this, [this]() {
        while(udpSocket->hasPendingDatagrams())
        {
            QByteArray datagram;
            datagram.resize(udpSocket->pendingDatagramSize());
            QHostAddress peerAddress;
            uint16_t peerPort;
            udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddress, &peerPort);

            QString string="[From"+peerAddress.toString()+":"+QString::number(peerPort);
            ui->textEdit->appendPlainText(string+datagram.data());
        }
    });

    //加入组播
    connect(ui->actStart,&QAction::triggered,this,[this](){
        QString IP=ui->comboIP->currentText();
        this->groupAddress=new QHostAddress(IP);
        uint16_t groupPort=ui->spinPort->value();
        // 默认情况下，Windows 不允许多个进程（或线程）绑定到同一个 IP 和端口
        // QUdpSocket::ReuseAddressHint 提示操作系统重用地址
        if(udpSocket->bind(QHostAddress::AnyIPv4, groupPort,  QUdpSocket::ReuseAddressHint))
        {
            udpSocket->joinMulticastGroup(*groupAddress);
            ui->textEdit->appendPlainText("加入组播成功");
            ui->textEdit->appendPlainText("组播地址IP"+IP);
            ui->textEdit->appendPlainText("绑定端口"+QString::number(groupPort));
            qDebug()<<"加入组播成功 "<<"组播地址IP "<<IP<<"绑定端口 "<<groupPort;

            ui->actStart->setEnabled(false);
            ui->actStop->setEnabled(true);
            ui->comboIP->setEnabled(false);
            ui->spinPort->setEnabled(false);
            ui->btnMulticast->setEnabled(true);
        }
        else
            {
            ui->textEdit->appendPlainText("绑定端口失败");
            qDebug()<<"绑定端口失败";
        }
    });

    //退出组播
    connect(ui->actStop,&QAction::triggered,this,[this](){
        this->udpSocket->leaveMulticastGroup(*groupAddress);
        this->udpSocket->abort();//解除绑定
        ui->textEdit->appendPlainText("绑定解除，退出组播");
        qDebug()<<"绑定解除，退出组播";

        ui->actStart->setEnabled(true);
        ui->actStop->setEnabled(false);
        ui->comboIP->setEnabled(true);
        ui->spinPort->setEnabled(true);
        ui->btnMulticast->setEnabled(false);
    });

    //发送
    connect(ui->btnMulticast,&QPushButton::clicked,this,[this](){
        uint16_t groupPort=ui->spinPort->value();
        QString message=ui->editMsg->text();

        this->udpSocket->writeDatagram(message.toUtf8(),*(this->groupAddress),groupPort);
        ui->textEdit->appendPlainText("[multicase]"+message);
        ui->editMsg->clear();
        ui->editMsg->setFocus();
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}
